#ifndef _COMPILATION_PHI_INSERTION_TASK_H
#define _COMPILATION_PHI_INSERTION_TASK_H

#include <compilation/Forward.h>

#include <vector>
#include <map>
#include <set>

#include <compilation/task/Task.h>
#include <compilation/task/Handler.h>
#include <compilation/task/Propagator.h>
#include <compilation/task/Phi.h>

#include <compilation/instruction/Expression.h>
#include <compilation/instruction/ExpressionVisitor.h>

#include <compilation/instruction/Instruction.h>
#include <compilation/instruction/InstructionVisitor.h>


// PhiInsertionTask and PhiInsertionAgenda.
namespace compilation
{

class PhiInsertionTask
    : public VirtualTask
{
public:

  typedef PhiLabelResult LabelResult;
  typedef PhiIsBefore IsBefore;

  typedef PhiLabelResult::ModifiedVariables ModifiedVariables;

  typedef std::vector<GotoInstruction*> DominationFrontier;

private:

  DominationFrontier* m_dominationFrontier;

  Scope m_scope;

  std::set<VirtualExpression*, IsBefore> m_modified;

  bool m_isLValue;

public:


  PhiInsertionTask(const Function& function)
    : m_dominationFrontier(NULL), m_scope(function.globalScope()), m_isLValue(false)
  { setInstruction(function.getFirstInstruction()); }

  PhiInsertionTask(GotoInstruction& gotoInstruction, const PhiInsertionTask& source)
    : m_dominationFrontier(&gotoInstruction.getDominationFrontier())
    , m_scope(source.m_scope), m_isLValue(false)
  { setInstruction(gotoInstruction); }

  PhiInsertionTask(const PhiInsertionTask& source)
    : VirtualTask(source)
    , m_scope(source.m_scope)
    , m_modified(source.m_modified)
    , m_isLValue(false) {}

  bool hasDominationFrontier(void) const
  { return m_dominationFrontier; }

  std::vector<GotoInstruction*> const* getDominationFrontier(void) const
  { return m_dominationFrontier; }

  std::vector<GotoInstruction*>* getDominationFrontier(void)
  { return m_dominationFrontier; }

  void setDominationFrontier(DominationFrontier* frontier)
  { m_dominationFrontier = frontier; }

  ModifiedVariables const& getModifiedVariables(void) const
  { return m_modified; }

  void addModifiedVariables(VirtualExpression* expr)
  { m_modified.insert(expr); }

  void addModifiedVariables(ModifiedVariables::const_iterator const& begin,
                            ModifiedVariables::const_iterator const& end)
  { m_modified.insert(begin, end); }

  void eraseModifiedVariables(ModifiedVariables::const_iterator const& begin,
                              ModifiedVariables::const_iterator const& end)
  { m_modified.erase(begin, end); }

  ModifiedVariables::const_iterator
  eraseModifiedVariables(ModifiedVariables::const_iterator const& it)
  { return m_modified.erase(it); }

  void setModifiedVariables(ModifiedVariables const& vars)
  { m_modified = vars; }

  Scope const& getScope(void) const
  { return m_scope; }

  Scope& getScope(void)
  { return m_scope; }

  void setScope(Scope const& scope)
  { m_scope = scope; }

  bool isLValue(void) const
  { return m_isLValue; }

  void setLValueStatement(bool isit)
  { m_isLValue = isit; }

  virtual VirtualTask* clone(void) const
  { return new PhiInsertionTask(*this); }
};

class PhiInsertionAgenda
    : public VirtualAgenda
{
public:

  typedef PhiInsertionTask Task;

  typedef InstructionVisitor<
  void, PhiInsertionTask&, PhiInsertionAgenda&, WorkListReusability&>
  instruction_handler_visitor_type;

  typedef ExpressionVisitor<
  void, PhiInsertionTask&, PhiInsertionAgenda&, WorkListReusability&>
  expression_handler_visitor_type;

  typedef InstructionVisitor<
  bool, PhiInsertionTask&, PhiInsertionAgenda&, WorkListReusability&>
  instruction_propagator_visitor_type;

private:

  std::vector<LabelInstruction*> m_labels;

public:

  instruction_handler_visitor_type const&
  handleInstruction;

  expression_handler_visitor_type const&
  handleExpression;

  instruction_propagator_visitor_type const&
  propagateOnUnmarked;

  PhiInsertionAgenda(
      instruction_handler_visitor_type const& instruction_handler_visitor,
      expression_handler_visitor_type const& expression_handler_visitor,
      instruction_propagator_visitor_type const& instruction_propagator_visitor,
      const Function& function)
    : handleInstruction(instruction_handler_visitor)
    , handleExpression(expression_handler_visitor)
    , propagateOnUnmarked(instruction_propagator_visitor)
  { addNewAsFirst(new PhiInsertionTask(function)); }

  virtual ~PhiInsertionAgenda(void);

  const std::vector<LabelInstruction*>& labels(void) const
  { return m_labels; }

  virtual void markInstructionWith(
      VirtualInstruction& instruction,
      VirtualTask& vtTask);
};

} // compilation

// PhiInsertionInstructionHandler.
namespace compilation
{

template <InstructionType _Type>
struct PhiInsertionInstructionHandler
    : public InstructionHandler<_Type, PhiInsertionTask, PhiInsertionAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      PhiInsertionTask& task,
      PhiInsertionAgenda& continuations,
      WorkListReusability& reuse)
  {
    InstructionHandler<_Type, PhiInsertionTask, PhiInsertionAgenda>
        ::operator ()(instr,task,continuations,reuse);
  }
};

template <>
struct PhiInsertionInstructionHandler<TExpression>
    : public InstructionHandler<TExpression, PhiInsertionTask, PhiInsertionAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      PhiInsertionTask& task,
      PhiInsertionAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct PhiInsertionInstructionHandler<TGoto>
    : public InstructionHandler<TGoto, PhiInsertionTask, PhiInsertionAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      PhiInsertionTask& task,
      PhiInsertionAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct PhiInsertionInstructionHandler<TLabel>
    : public InstructionHandler<TLabel, PhiInsertionTask, PhiInsertionAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      PhiInsertionTask& task,
      PhiInsertionAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct PhiInsertionInstructionHandler<TEnterBlock>
    : public InstructionHandler<TEnterBlock, PhiInsertionTask, PhiInsertionAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      PhiInsertionTask& task,
      PhiInsertionAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct PhiInsertionInstructionHandler<TExitBlock>
    : public InstructionHandler<TExitBlock, PhiInsertionTask, PhiInsertionAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      PhiInsertionTask& task,
      PhiInsertionAgenda& continuations,
      WorkListReusability& reuse);
};

} // compilation

// PhiInsertionExpressionHandler.
namespace compilation
{

template <ExpressionType _Type>
struct PhiInsertionExpressionHandler
    : public ExpressionHandler<_Type, PhiInsertionTask, PhiInsertionAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      PhiInsertionTask& task,
      PhiInsertionAgenda& continuations,
      WorkListReusability& reuse)
  {
    ExpressionHandler<_Type, PhiInsertionTask, PhiInsertionAgenda>
        ::operator ()(expr,task,continuations,reuse);
  }
};

template <>
struct PhiInsertionExpressionHandler<TLocalVariable>
    : public ExpressionHandler<TLocalVariable, PhiInsertionTask, PhiInsertionAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      PhiInsertionTask& task,
      PhiInsertionAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct PhiInsertionExpressionHandler<TAssign>
    : public ExpressionHandler<TAssign, PhiInsertionTask, PhiInsertionAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      PhiInsertionTask& task,
      PhiInsertionAgenda& continuations,
      WorkListReusability& reuse);
};

} // compilation

// PhiInsertionPropagator.
namespace compilation
{

template <InstructionType _Type>
struct PhiInsertionPropagator
    : public InstructionPropagator<_Type, PhiInsertionTask, PhiInsertionAgenda>
{
  bool operator ()(
      VirtualInstruction* instr,
      PhiInsertionTask& task,
      PhiInsertionAgenda& continuations,
      WorkListReusability& reuse)
  {
    return InstructionPropagator<_Type, PhiInsertionTask, PhiInsertionAgenda>
        ::operator ()(instr,task,continuations,reuse);
  }
};

template <>
struct PhiInsertionPropagator<TGoto>
    : public InstructionPropagator<TGoto, PhiInsertionTask, PhiInsertionAgenda>
{
  bool operator ()(
      VirtualInstruction* instr,
      PhiInsertionTask& task,
      PhiInsertionAgenda& continuations,
      WorkListReusability& reuse);
};

} // compilation

#endif // _COMPILATION_PHI_INSERTION_TASK_H
